using System;
using System.Collections.Generic;
using LitJson2;
using Net.Common;
using UnityEngine;

/****************************************************
// 功能：网络管理器
// | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
// | Copyright (c) 2021-2025 聊来 All rights reserved.
// | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">聊来</a> )
// | @Author: 贯彻己道的云 <18970881148@qq.com>
// | DateTime: 2025/2/11 14:17:36
*****************************************************/

public class NetManager : SingletonPatternMonoBase<NetManager>, IManager
{
    private ClientTcp clientTcp;
    private ClientKcp clientKcp;
    private WebSocketClient clientWebsocket;
    private readonly Dictionary<RequestType, Action<MsgInfo>> callbackDic = new Dictionary<RequestType, Action<MsgInfo>>();
    private uint sendPingId = 0;
    private uint pingCounter = 0;
    private Dictionary<uint, DateTime> pingDic = new Dictionary<uint, DateTime>();
    
    /// <summary>
    /// 初始化TCP、UDP(KCP)请求
    /// </summary>
    public void InitManager()
    {
        clientTcp = ClientTcp.Instance;
        clientTcp.Connect();
        ClientTcp.handleController += DispatchMsg;
        
        clientWebsocket = WebSocketClient.Instance;
        clientWebsocket.Connect();
        WebSocketClient.handleController += DispatchMsg;

        RegisterRequestCallback();
    }

    private void DispatchMsg(MsgInfo msgInfo)
    {
        if (callbackDic.ContainsKey(msgInfo.requestType))
        {
            callbackDic[msgInfo.requestType].Invoke(msgInfo);
        }
    }

    /// <summary>
    /// 注册服务器消息回调
    /// </summary>
    private void RegisterRequestCallback()
    {
        callbackDic.Add(RequestType.CONNECTED, OnConnected);
        callbackDic.Add(RequestType.CONNECTION_REFUSED, LoginController.Instance.OnConnectionRefused);
        callbackDic.Add(RequestType.LOGIN, LoginController.Instance.OnLogin);
        callbackDic.Add(RequestType.MATCH, MatchController.Instance.OnMatch);
        callbackDic.Add(RequestType.READY_ROOM, RoomController.Instance.OnReadyRoom);
        callbackDic.Add(RequestType.UPDATE_ROOM, RoomController.Instance.OnUpdateRoom);
        callbackDic.Add(RequestType.SELECT_HERO, HeroController.Instance.OnLoadBattleHero);
        callbackDic.Add(RequestType.LOAD_HERO_PROGRESS, HeroController.Instance.OnLoadHeroProgress);
        callbackDic.Add(RequestType.BATTLE_START, BattleController.Instance.OnBattleStart);
        callbackDic.Add(RequestType.BATTLE_END, BattleController.Instance.OnBattleEnd);
        callbackDic.Add(RequestType.SEND_OP_KEY, BattleController.Instance.OnNtfOpKey);
        callbackDic.Add(RequestType.SEND_CHAT, BattleController.Instance.OnChat);
        callbackDic.Add(RequestType.KCP_LOGIN, OnKcpLogin);
        callbackDic.Add(RequestType.KCP_PING, OnKcpPing);
    }

    /// <summary>
    /// TCP服务器网络连接成功、在初始化KCP
    /// </summary>
    /// <param name="msgInfo"></param>
    private void OnConnected(MsgInfo msgInfo)
    {
        var jsonData = JsonMapper.ToObject(msgInfo.data);

        //EventDispatcher.Instance.DispatchEvent(Constants.CONNECT_SUCCEED, null);
        ModelManager.Instance.isConnected = true;
        ModelManager.Instance.conv = (int) jsonData["conv"];
        Debug.Log("[连接服务器成功。。。]");
        RandomUtils.InitRandom((int) jsonData["random"]);
        clientKcp = ClientKcp.Instance;
        clientKcp.Connect();
    }
    
    
    /// <summary>
    /// 太恶心了
    /// </summary>
    /// <param name="msgInfo"></param>
    private void OnKcpLogin(MsgInfo msgInfo)
    {
        InvokeRepeating(nameof(StartHeartbeat), 5,5);
    }
    
    /// <summary>
    /// KCP心跳检测
    /// </summary>
    private void StartHeartbeat()
    {
        
        ++sendPingId;
        var data = new PingRequest { pingId = sendPingId };
        SendByKcp(RequestType.KCP_PING, data);
        
        if (pingDic.Count > 0)
        {
            ++pingCounter;
            if (pingCounter >= 3)
            {
                UIManager.Instance.ShowTips("网络异常，检测手机网络环境");
                pingCounter = 0;
            }
        }
        pingDic.Add(sendPingId, DateTime.Now);
    }
    
    /// <summary>
    /// KCP心跳回应
    /// </summary>
    /// <param name="msgInfo"></param>
    private void OnKcpPing(MsgInfo msgInfo)
    {
        var response = JsonUtility.FromJson<PingResponse>(msgInfo.data);

        var pingId = response.pingId;
        if (pingDic.ContainsKey(pingId))
        {
            var time = DateTime.Now - pingDic[pingId];
            ModelManager.Instance.netDelay = (int) time.TotalMilliseconds;
            pingDic.Clear();
            pingCounter = 0;
        }
        else
        {
            Debug.LogError($"Net Ping ID Error:{pingId}");
        }
    }

    #region 发送消息

    /// <summary>
    /// 发送TCP消息
    /// </summary>
    /// <param name="type"></param>
    /// <param name="data"></param>
    public void SendByTcp(RequestType type, object data)
    {
        clientTcp.Send(new MsgInfo()
        { 
            requestType = type,
            data = JsonUtility.ToJson(data)
        });
    }

    /// <summary>
    /// 发送kcp消息
    /// </summary>
    /// <param name="type"></param>
    /// <param name="data"></param>
    /// <param name="channelId"></param>
    public void SendByKcp(RequestType type, object data, string channelId = "")
    {
        clientKcp.Send(new MsgInfo()
        {
            requestType = type,
            data = JsonUtility.ToJson(data),
            channelId = channelId
        });
        
    }

    /// <summary>
    /// 发送WebSocket消息
    /// </summary>
    /// <param name="type"></param>
    /// <param name="data"></param>
    public void SendByWebSocket(RequestType type, object data)
    {
        clientWebsocket.Send(new MsgInfo()
        { 
            requestType = type,
            data = JsonUtility.ToJson(data)
        });
    }

    #endregion
    

    private void FixedUpdate()
    {
        clientTcp.Dispatch();
        clientWebsocket.Dispatch();
    }


    private void OnDestroy()
    {
        clientTcp.Disconnect();
        clientKcp.Disconnect();
        CancelInvoke(nameof(StartHeartbeat));
        clientWebsocket.Disconnect();
    }
}